/*
 * OmniThreads.hh
 *
 * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved.
 * Copyright 2002, Bastiaan Bakker. All rights reserved.
 *
 * See the COPYING file for the terms of usage and distribution.
 */

#ifndef _LOG4CPP_THREADING_OMNITHREADS_HH
#define _LOG4CPP_THREADING_OMNITHREADS_HH

#include <log4cpp/Portability.hh>
#include <omnithread.h>
#include <stdio.h>
#include <string>

LOG4CPP_NS_BEGIN
namespace threading {
    /**
     * Return an identifier for the current thread. What these
     * identifiers look like is completely up to the underlying
     * thread library. OmniThreads returns the POSIX thread Id.
     **/
    std::string getThreadId();

    /**
     * A simple, non recursive Mutex.
     * Equivalent to Boost.Threads boost::mutex
     **/
    typedef omni_mutex Mutex;

    /**
     * A simple "resource acquisition is initialization" idiom type lock
     * for Mutex.
     * Equivalent to Boost.Threads boost::scoped_lock.
     **/
    typedef omni_mutex_lock ScopedLock;

    /**
     * This class holds Thread local data of type T, i.e. for each
     * thread a ThreadLocalDataHolder holds 0 or 1 instance of T.
     * The held object must be heap allocated and will be deleted
     * upon termination of the thread to wich it belongs.
     * This is an omni_threads based equivalent of Boost.Threads
     * thread_specific_ptr<T> class.
     **/
    template<typename T> class ThreadLocalDataHolder {
    public:
        typedef T data_type;

        inline ThreadLocalDataHolder() :
            _key(omni_thread::allocate_key()) {};

        inline ~ThreadLocalDataHolder() {};

        /**
         * Obtains the Object held for the current thread.
         * @return a pointer to the held Object or NULL if no
         * Object has been set for the current thread.
         **/
        inline T* get() const {
            Holder* holder = dynamic_cast<Holder*>(
                ::omni_thread::self()->get_value(_key));
            return (holder) ? holder->data : NULL;
        };

        /**
         * Obtains the Object held for the current thread.
         * Initially each thread holds NULL.
         * @return a pointer to the held Object or NULL if no
         * Object has been set for the current thread.
         **/
        inline T* operator->() const { return get(); };

        /**
         * Obtains the Object held for the current thread.
         * @pre get() != NULL
         * @return a reference to the held Object.
         **/
        inline T& operator*() const { return *get(); };

        /**
         * Releases the Object held for the current thread.
         * @post get() == NULL
         * @return a pointer to the Object thas was held for
         * the current thread or NULL if no Object was held.
         **/
        inline T* release() {
            T* result = NULL;
            Holder* holder = dynamic_cast<Holder*>(
                ::omni_thread::self()->get_value(_key));

            if (holder) {
                result = holder->data;
                holder->data = NULL;
            }

            return result;
        };

        /**
         * Sets a new Object to be held for the current thread. A
         * previously set Object will be deleted.
         * @param p the new object to hold.
         * @post get() == p
         **/
        inline void reset(T* p = NULL) {
            Holder* holder = dynamic_cast<Holder*>(
                ::omni_thread::self()->get_value(_key));
            if (holder) {
                if (holder->data)
                    delete holder->data;

                holder->data = p;
            }
            else {
                holder = new Holder(p);
                ::omni_thread::self()->set_value(_key, holder);
            }
        };

    private:
        class Holder : public omni_thread::value_t {
        public:
            Holder(data_type* data) : data(data) {};
            virtual ~Holder() { if (data) delete (data); };
            data_type* data;
        private:
            Holder(const Holder& other);
            Holder& operator=(const Holder& other);
        };

        omni_thread::key_t _key;
    };
}
LOG4CPP_NS_END
#endif
